/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2019.
 * Description: vector_page_wrprotect.c
 * Author: zhanghan <zhanghan23@huawei.com>
 * Create: 2019-09-24
 */

#include <asm/tls.h>
#include <linux/init.h>
#include <linux/pgtable.h>
#include <asm/traps.h>
#include <asm/tlbflush.h>
#ifdef CONFIG_RTOS_HAL_VECTOR_PROT_MEM_SIZE_OPTIMIZE
#include <asm/set_memory.h>
#endif
#include <linux/hal/vector_page_wrprotect.h>

#ifndef CONFIG_RTOS_HAL_VECTOR_PROT_MEM_SIZE_OPTIMIZE
static void flush_pmd_entry_new(void *pmd)
{
	flush_pmd_entry(pmd);

	/* Invalidate entire TLB Inner Shareable */
	asm("mcr p15, 0, %0, c8, c6, 0" : : "r" (0) : "cc");

	/* Invalidate data TLB */
	asm("mcr p15, 0, %0, c8, c3, 0" : : "r" (0) : "cc");

	dsb(ishst);
}

static int vecpage_wrprotect;

int get_vectors_page_wrprotect(void)
{
	return vecpage_wrprotect;
}

static int __init set_vectors_page_wrprotect(char *str)
{
	vecpage_wrprotect = 1;
	return 1;
}
early_param("vecpage_wrprotect", set_vectors_page_wrprotect);
#endif

/*
 * This function needs to be used with vectors_page_wrprotect(),
 * and both functions, as well as code within the scope of both
 * functions, need to be used when preemption is disabled.
 */
void vectors_page_wrable(void)
{
#ifdef CONFIG_RTOS_HAL_VECTOR_PROT_MEM_SIZE_OPTIMIZE
	set_kernel_text_rw();
#else
	pgd_t *pgdp = NULL;
	pmd_t *pmdp = NULL;

	if (!vecpage_wrprotect)
		return;
	pgdp = pgd_offset(current->active_mm, (unsigned long)(uintptr_t)vectors_page);
	pmdp = pmd_offset((pud_t *)pgdp, (unsigned long)(uintptr_t)vectors_page);
	pmd_val(*pmdp) &= ~(1<<MAP_BIT_APX);
	flush_pmd_entry_new(pmdp);
#endif
}
EXPORT_SYMBOL(vectors_page_wrable);

/*
 * This function needs to be used with vectors_page_wrable(),
 * and both functions, as well as code within the scope of both
 * functions, need to be used when preemption is disabled.
 */
void vectors_page_wrprotect(void)
{
#ifdef CONFIG_RTOS_HAL_VECTOR_PROT_MEM_SIZE_OPTIMIZE
	set_kernel_text_ro();
#else
	pgd_t *pgdp = NULL;
	pmd_t *pmdp = NULL;

	if (!vecpage_wrprotect)
		return;
	pgdp = pgd_offset(current->active_mm, (unsigned long)(uintptr_t)vectors_page);
	pmdp = pmd_offset((pud_t *)pgdp, (unsigned long)(uintptr_t)vectors_page);
	pmd_val(*pmdp) |= (1<<MAP_BIT_APX);
	flush_pmd_entry_new(pmdp);
#endif
}
EXPORT_SYMBOL(vectors_page_wrprotect);

#ifndef CONFIG_RTOS_HAL_VECTOR_PROT_MEM_SIZE_OPTIMIZE
void vectors_page_wrprotect_init(void)
{
	pgd_t *pgdp = NULL;
	pmd_t *pmdp = NULL;

	if (!vecpage_wrprotect)
		return;
	pgdp = pgd_offset(&init_mm, (unsigned long)(uintptr_t)vectors_page);
	pmdp = pmd_offset((pud_t *)pgdp, (unsigned long)(uintptr_t)vectors_page);
	pmd_val(*pmdp) |= (1<<MAP_BIT_APX);
	flush_pmd_entry_new(pmdp);
}
#endif
